package com.bank.service.impl;

import com.bank.constant.AccountStatus;
import com.bank.constant.AccountType;
import com.bank.constant.LoanType;
import com.bank.constant.MessageConstant;
import com.bank.dto.AccountDTO;
import com.bank.dto.LoanDTO;
import com.bank.dto.RepayDTO;
import com.bank.dto.UserUpdateInfoDTO;
import com.bank.entity.*;
import com.bank.exception.*;
import com.bank.mapper.*;
import com.bank.service.AccountService;
import com.bank.service.LoanService;
import com.bank.vo.LoanTransactionVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;


@Service
@Slf4j
public class LoanServiceImpl implements LoanService {
    @Autowired
    private LoanMapper loanMapper;
    @Autowired
    private AccountMapper accountMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private LoanTransactionMapper loanTransactionMapper;
    @Autowired
    private BillMapper billMapper;

    @Override
    public Loan getLoanByAccountId(Long accountId) {
        return loanMapper.getLoanByAccountId(accountId);
    }

    @Override
    public void addLoanByAccountId(Long accountId) {
        loanMapper.addLoanByAccountId(accountId);
    }

    public Account checkAuthentication(Long accountId) {
        Account account = accountMapper.getAccountById(accountId);
        // 账户不存在
        if (account == null) {
            throw new AccountNotFoundException(MessageConstant.ACCOUNT_NOT_FOUND);
        }

        // 账户状态不正常
        if (!(Objects.equals(account.getStatus(), AccountStatus.NORMAL) &&
                Objects.equals(account.getAccountType(), AccountType.CREDIT))) {
            throw new AccountStatusInvalidException(MessageConstant.ACCOUNT_STATUS_INVALID);
        }

        // 账户不为信用卡
        if (!Objects.equals(account.getAccountType(), AccountType.CREDIT)) {
            throw new AuthenticationFailedException(MessageConstant.ACCOUNT_STATUS_INVALID);
        }

        return account;
    }

    @Override
    public void getLoan(LoanDTO loanDTO) {
        Account account = checkAuthentication(loanDTO.getAccountId());

        // 身份验证
        Long userId = account.getUserId();
        User user = userMapper.getById(userId);
        if (!(Objects.equals(user.getRealName(), loanDTO.getRealName()) && Objects.equals(user.getIdNumber(), loanDTO.getIdNumber()))) {
            // 身份验证失败
            throw new AuthenticationFailedException(MessageConstant.AUTHENTICATION_FAILED);
        }

        // 密码验证
        if (!Objects.equals(DigestUtils.md5DigestAsHex(loanDTO.getPassword().getBytes()), account.getPassword())) {
            // 密码验证失败
            throw new AuthenticationFailedException(MessageConstant.PASSWORD_ERROR);
        }

        // 借款金额是否超过额度
        BigDecimal loanAlready = loanTransactionMapper.getLoansByAccountId(loanDTO.getAccountId());
        if (loanAlready != null &&
                loanMapper.getLoanByAccountId(loanDTO.getAccountId()).getLoanLimit()
                        .compareTo(loanAlready.add(loanDTO.getLoan())) < 0) {
            // 借款金额超过额度
            throw new LoanLimitExceededException(MessageConstant.LOAN_LIMIT_EXCEEDED);
        } else if (loanAlready == null &&
                loanMapper.getLoanByAccountId(loanDTO.getAccountId()).getLoanLimit()
                        .compareTo(loanDTO.getLoan()) < 0) {
            throw new LoanLimitExceededException(MessageConstant.LOAN_LIMIT_EXCEEDED);
        }
        log.info("loanDTO: {}", loanDTO);
        log.info("loanAlready: {}", loanAlready);

        Loan loan = loanMapper.getLoanByAccountId(loanDTO.getAccountId());
        Bill bill = billMapper.getBillByAccountId(loanDTO.getAccountId());
        bill.setBalance(bill.getBalance().add(loanDTO.getLoan()));
        account.setUpdateTime(LocalDateTime.now());
        // 借款
        synchronized (this) {
            // 更新借款金额
            loanMapper.updateLoanByAccountId(loanDTO.getAccountId(), loanDTO.getLoan().add(loan.getLoan()));
            // 更新账单
            billMapper.setBalanceByAccountId(bill);
            // 更新账户状态
            accountMapper.updateTimeByAccountId(account.getAccountId(), account.getUpdateTime());
        }

        // 插入借款记录
        LoanTransaction loanTransaction = LoanTransaction.builder()
                .accountId(loanDTO.getAccountId())
                .amount(loanDTO.getLoan())
                .balance(bill.getBalance())
                .loanType(LoanType.LOAN)
                .loanTime(LocalDateTime.now())
                .repayTime(LocalDateTime.now().plusDays(1))
                .remark(loanDTO.getRemark())
                .build();
        loanTransactionMapper.insert(loanTransaction);
    }

    @Override
    public List<LoanTransactionVO> getAllLoanTransaction(Long accountId) {
        Account account = checkAuthentication(accountId);
        return loanTransactionMapper.getLoanInfoByAccountId(accountId);
    }

    @Override
    public void repayLoan(RepayDTO repayDTO) {
        LoanTransaction loanTransaction = loanTransactionMapper.getLoanInfoByTransactionId(repayDTO.getTransactionId());

        if (!(LoanType.LOAN.equals(loanTransaction.getLoanType()) || LoanType.OVER_EXPIRED.equals(loanTransaction.getLoanType()))) {
            throw new LoanTypeInvalidException(MessageConstant.LOAN_TYPE_INVALID);
        }

        if (loanTransaction.getAmount().compareTo(repayDTO.getRepay()) != 0) {
            throw new RepayNotEnoughException(MessageConstant.REPAY_NOT_ENOUGH);
        }

        Loan loan = loanMapper.getLoanByAccountId(loanTransaction.getAccountId());
        loan.setLoan(loan.getLoan().subtract(repayDTO.getRepay()));

        Bill bill = billMapper.getBillByAccountId(loanTransaction.getAccountId());
        loanTransaction.setLoanType(LoanType.REPAID_ALREADY);
        // 还款
        synchronized (this) {
            // 更新借款记录状态
            loanTransactionMapper.updateLoanType(loanTransaction.getTransactionId(), loanTransaction.getLoanType());
            // 更新明细表
            loanMapper.updateLoanByAccountId(loan.getAccountId(), loan.getLoan());
            // 更新账户状态
            accountMapper.updateTimeByAccountId(loan.getAccountId(), LocalDateTime.now());
        }

        // 插入还款记录
        LoanTransaction newLoanTransaction = LoanTransaction.builder()
                .accountId(loanTransaction.getAccountId())
                .amount(repayDTO.getRepay())
                .balance(bill.getBalance())
                .loanType(LoanType.REPAY)
                .loanTime(LocalDateTime.now())
                .remark(repayDTO.getRemark())
                .build();

        loanTransactionMapper.insert(newLoanTransaction);
    }
}
